<?php
// +----------------------------------------------------------------------
// | ManniuRead
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://manniuyun.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: heitao <read@manniuyun.com>
// +----------------------------------------------------------------------

namespace app\book\model;

use manniu\model\Base;

use think\Request;
use think\Db;
use manniu\controller\Cache;
use app\book\model\Book;

class Chapter extends Base{
	// 开启自动写入时间戳字段
	protected $autoWriteTimestamp = true;
	// 定义时间戳字段名
    protected $createTime = 'create_time';
    protected $updateTime = 'update_time';

    // 章节缓存名
    protected $chapterCacheKey = 'chapter{id}';
    // 章节缓存redis类型
    protected $chapterRedisType = 'hash';
    // 章节缓存过期时间
    protected $chapterCacheTime = 300;
    // 章节列表缓存名
    protected $chapterListCacheKey = 'chapterList{bid}';
    // 章节列表缓存redis类型
    protected $chapterListRedisType = 'sortedSet';
    // 章节列表缓存过期时间
    protected $chapterListCacheTime = 300;
    // 设置重置本书章节排序的标识
    protected $resetDisplayOrder = false;

    // 模型事件
    protected static function init(){
        // 钩子 - 新增前
        Chapter::beforeInsert(function ($chapter) {
            // 排序值不存在时生成排序值
            if(!isset($chapter->displayorder) || !$chapter->displayorder){
                $chapter->displayorder = Db::name('chapter')->where([
                        'book_id' => $chapter->getData('book_id')
                    ])->max('displayorder')+1;
            }
            // 如果新增章节时分卷不是最新分卷则重置本书章节排序
            $book_id = $chapter->book_id;
            $book = Book::get(function ($query) use ($book_id){
                $query->field('volumes')->where('id', $book_id);
            });
            $volumes = $book->volumes;
            if(is_array($volumes)){
                krsort($volumes);
                if($chapter->volume != key($volumes)){
                    $chapter->resetDisplayOrder = true;
                }
            }
            $chapter->rmChapterListCache($chapter->getData('book_id'));
        });
        // 钩子 - 新增后
        Chapter::afterInsert(function ($chapter) {
            // 生成章节url
            $update_param['url'] = str_replace(Request::instance()->baseFile(), '', url('book/Book/chapter',['bid'=>$chapter->getData('book_id'),'cid'=>$chapter->getData('id')]));
            Db::name('chapter')->where('id', $chapter->getData('id'))->update($update_param);
            // 重新生成上一章、下一章缓存
            $chapter->setPrveAndNextChaptersCache($chapter->getData('book_id'), $chapter->getData('displayorder'));
        });
        // 钩子 - 更新前
        Chapter::beforeUpdate(function ($chapter) {
            if($chapter->volume != $chapter->origin['volume']){
                $chapter->resetDisplayOrder = true;
            }
            $chapter->rmChapterCache($chapter->getData('id'));
        });
        // 钩子 - 写入前
        Chapter::beforeWrite(function ($chapter) {
            // 统计章节字数
            $content = isset($chapter->content) ? $chapter->content : $chapter->chapter_data->content;
            $chapter->words = mb_strlen(mn_clearhtml($content));
            // 根据字数计算价格
            $price = (int)ceil($chapter->getAttr('words') / config('mn_book.chapter_words_fees'));
            $chapter->price = $price<0 ? 0 : $price;
            // 判断是否重置本书章节排序
            if($chapter->resetDisplayOrder === true){
                $chapter->rmAllChapterCache($chapter->getData('book_id'));
                $chapter->rmChapterListCache($chapter->getData('book_id'));
            }
        });
        // 钩子 - 写入后
        Chapter::afterWrite(function ($chapter) {
            if($chapter->resetDisplayOrder === true){
                $chapter->resetDisplayOrderByBookId($chapter->getData('book_id'));
                // $chapter->setAllChaptersCache($chapter->getData('book_id'));
            }
            $chapter->countBookSumWorks($chapter->getData('book_id'));
            $chapter->setChapterCache($chapter->getData('id'));
        });
        // 钩子 - 删除前
        Chapter::beforeDelete(function ($chapter) {
            $chapter->rmChapterListCache($chapter->getData('book_id'));
            $chapter->rmChapterCache($chapter->getData('id'));
        });
        // 钩子 - 删除后
        Chapter::afterDelete(function ($chapter) {
            $chapter->countBookSumWorks($chapter->getData('book_id'));
            $chapter->setPrveAndNextChaptersCache($chapter->getData('book_id'), $chapter->getData('displayorder'));
        });
    }

    // 一对一关联章节内容表
    public function chapterData(){
        return $this->hasOne('ChapterData','chapter_id','id');
    }

    /**
     * 获取章节数据
     * @param int $id 章节id
     * @param int $time sql查询缓存标识/缓存时间
     * @return array|bool|null
     */
    public function getChater($id = 0, $time = 60){
        $id = (int)$id;
        if(!$id){
            return null;
        }
        $chapter = $this->getChapterCache($id);
        if(!$chapter){
            $chapter = $this->setChapterCache($id, $time, true);
        }
        return $chapter;
    }

    /**
     * 获取章节列表
     * @param int $book_id 图书id
     * @param int $time sql查询缓存标识/缓存时间
     * @return null
     */
    public function getChapterList($book_id = 0, $time = 60){
        $book_id = (int)$book_id;
        if(!$book_id){
            return null;
        }
        $chapter_list = $this->getChapterListCache($book_id);
        if(!$chapter_list){
            $chapter_list = $this->setChapterListCache($book_id, $time, true);
        }
        return $chapter_list;
    }

    /**
     * 获取章节缓存
     * @param int $id 章节id
     * @return bool
     */
    protected function getChapterCache($id){
        return Cache::get($this->formatCacheKey($this->chapterCacheKey, ['id'=>$id]), $this->chapterRedisType);
    }

    /**
     * 删除章节缓存
     * @param $id 章节id
     */
    protected function rmChapterCache($id){
        Cache::rm($this->formatCacheKey($this->chapterCacheKey, ['id'=>$id]));
    }

    /**
     * 生成章节缓存
     * @param int $id 章节id
     * @param bool $cache 缓存/缓存时间
     * @param bool $is_return 是否返回查询数据
     * @param array $hidden_field 隐藏属性
     * @return array
     */
    protected function setChapterCache($id, $cache = false, $is_return = false, $hidden_field = ['update_time']){
        $chapter_info = [];
        $chapter = $this->where(['id'=>$id])->cache($cache)->find();
        if($chapter){
            $chapter_info = $chapter->hidden($hidden_field)->toArray();
            // 获取上一页链接
            $chapter_info['prev_page'] = $this->getPrevChapter($chapter->getData('book_id'), $chapter->getData('displayorder'));
            // 获取下一页链接
            $chapter_info['next_page'] = $this->getNextChapter($chapter->getData('book_id'), $chapter->getData('displayorder'));
            Cache::set($this->formatCacheKey($this->chapterCacheKey, ['id'=>$id]), $chapter_info, $this->chapterCacheTime, $this->chapterRedisType);
        }
        if($is_return === true){
            return $chapter_info ? $chapter_info : false;
        }
    }

    /**
     * 根据图书id与章节排序值查询 - 上一章
     * @param int $book_id 图书id
     * @param int $displayorder 章节排序值
     */
    protected function getPrevChapter($book_id, $displayorder, $field = ['chapter_name', 'url']){
        // 获取上一页链接
        $prev_chapter = $this->field($field)
            ->where('book_id', $book_id)
            ->where('displayorder', '<', $displayorder)
            ->order('displayorder desc')
            ->find();
        $prev_chapter = $prev_chapter ? $prev_chapter->toArray() : null;
        return $prev_chapter;
    }

    /**
     * 根据图书id与章节排序值查询 - 下一章
     * @param int $book_id 图书id
     * @param int $displayorder 章节排序值
     */
    protected function getNextChapter($book_id, $displayorder, $field = ['chapter_name', 'url']){
        // 获取上一页链接
        $next_chapter = $this->field($field)
            ->where('book_id', $book_id)
            ->where('displayorder', '>', $displayorder)
            ->order('displayorder asc')
            ->find();
        $next_chapter = $next_chapter ? $next_chapter->toArray() : null;
        return $next_chapter;
    }

    /**
     * 生成所有章节缓存
     * @param $book_id  图书id
     */
    protected function setAllChaptersCache($book_id) {
        $chapters = $this->field('id')->where('book_id',$book_id)->select();
        if ($chapters){
            foreach ($chapters as $key => $chapter) {
                $this->setChapterCache($chapter->getData('id'));
            }
        }
    }

    /**
     * 删除所有章节缓存
     * @param $book_id 图书id
     */
    protected function rmAllChapterCache($book_id) {
        $chapters = $this->field('id')->where('book_id',$book_id)->select();
        if ($chapters){
            foreach ($chapters as $key => $chapter) {
                $this->rmChapterCache($chapter->getData('id'));
            }
        }
    }

    /**
     * 获取章节列表缓存
     * @param int $book_id 图书id
     */
    protected function getChapterListCache($book_id){
        return Cache::get($this->formatCacheKey($this->chapterListCacheKey, ['bid'=>$book_id]), $this->chapterListRedisType, ['order'=>'asc']);
    }

    /**
     * 生成章节列表缓存
     * @param int $book_id 图书id
     * @param bool $cache 缓存/缓存时间
     * @param bool $is_return 是否返回查询数据
     * @param array $hidden_field 隐藏属性
     */
    protected function setChapterListCache($book_id, $cache = false, $is_return = false, $hidden_field = ['update_time']){
        $chapter_list = [];
        $data = $this->field(['id','chapter_name','charge','volume','url','create_time'])->where(['book_id'=>$book_id,'status'=>1])->order('displayorder asc')->cache($cache)->select();
        if($data){
            foreach ($data as $chapter){
                $chapter_list[] = $chapter->hidden($hidden_field)->toArray();
            }
            Cache::set($this->formatCacheKey($this->chapterListCacheKey, ['bid' => $book_id]), $chapter_list,$this->chapterListCacheTime, $this->chapterListRedisType);
        }
        if($is_return === true){
            return $chapter_list ? $chapter_list : false;
        }
    }

    /**
     * 删除章节列表缓存
     * @param int $book_id 图书id
     */
    protected function rmChapterListCache($book_id){
        Cache::rm($this->formatCacheKey($this->chapterListCacheKey, ['bid'=>$id]), $this->chapterListRedisType);
    }

    /**
     * 生成上一章、下一章缓存
     * @param int $book_id 图书id
     * @param int $displayorder 章节排序值
     * @param array $filed
     */
    protected function setPrveAndNextChaptersCache($book_id, $displayorder, $filed = ['id']){
        // 重新获取上一章、下一章信息
        $prev_chapter = $this->getPrevChapter($book_id, $displayorder, $filed);
        $next_chapter = $this->getNextChapter($book_id, $displayorder, $filed);
        if($prev_chapter){
            $this->rmChapterCache($prev_chapter['id']);
            $this->setChapterCache($prev_chapter['id']);
        }
        if($next_chapter){
            $this->rmChapterCache($next_chapter['id']);
            $this->setChapterCache($next_chapter['id']);
        }
    }

    /**
     * 重置章节排序
     * @param $book_id 图书id
     */
    protected function resetDisplayOrderByBookId($book_id) {
        // 获取章节列表
        $chapter_ids = Db::name('chapter')->field('id')->where(['book_id'=>$book_id])->order('volume asc,displayorder asc,id asc')->select();
        if($chapter_ids){
            $i = 1;
            foreach ($chapter_ids as $key => $value) {
                Db::name('chapter')->where('id',$value['id'])->update(['displayorder'=>$i]);
                $i++;
            }
        }
    }

    /**
     * 统计图书总字数
     * @param $book_id 图书id
     */
    protected function countBookSumWorks($book_id){
        $book = new Book;
        $book->countSumWorks($book_id);
    }
}